package cn.xy.base.server.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.xy.base.server.config.AppConfig;
import cn.xy.base.server.dao.SysAdminDao;
import cn.xy.base.server.dto.SysAdminAddDTO;
import cn.xy.base.server.dto.SysAdminUpdateDTO;
import cn.xy.base.server.enums.RoleEnum;
import cn.xy.base.server.po.SysAdmin;
import cn.xy.base.server.po.SysPermission;
import cn.xy.base.server.po.SysRole;
import cn.xy.base.server.service.SysAdminService;
import cn.xy.base.server.service.SysPermissionService;
import cn.xy.base.server.service.SysRoleService;
import cn.xy.base.server.vo.SysRoleVO;
import cn.xy.commons.cache.CacheManager;
import cn.xy.commons.constant.Consts;
import cn.xy.commons.enums.DisableStatus;
import cn.xy.commons.exception.JzRuntimeException;
import cn.xy.commons.util.JwtUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rustic.server.po.Rustic;
import com.rustic.server.po.RusticAdmin;
import com.rustic.server.service.RusticAdminService;
import com.rustic.server.service.RusticService;
import com.rustic.server.vo.RusticAdminDetailVO;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * SysAdmin对应的服务类实现
 *
 * @author auto
 */

@Service
@Slf4j
public class SysAdminServiceImpl extends ServiceImpl<SysAdminDao, SysAdmin> implements SysAdminService {

    /**
     * 12小时不操作即退出
     */
    private static final int MAX_LIFE = 12 * 60 * 60;
    private static final String LOGIN_PREFIX = "admin_";

    private static final int LOGIN_LIMIT = 5;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private RusticAdminService rusticAdminService;

    @Autowired
    private RusticService rusticService;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysPermissionService sysPermissionService;

    @Override
    public SysAdmin getByToken(String token, boolean ignoreAuth) {
        Claims claims = JwtUtil.parseJwt(appConfig.getJwtSecret(), token);
        if (!ignoreAuth && JwtUtil.isExpired(claims, appConfig.getJwtSubject())) {
            throw new JzRuntimeException(Consts.NO_LOGIN_CODE, "未登录");
        }
        if (claims != null) {
            int adminId = Integer.parseInt(String.valueOf(claims.get(SysAdminService.ID)));
            boolean isLogin = this.isLogin(adminId, token);
            if (!ignoreAuth && !isLogin) {
                throw new JzRuntimeException(Consts.NO_LOGIN_CODE, "未登录");
            }
            this.renewLogin(adminId, token);
            return getById(adminId);
        }
        return null;
    }

    @Override
    public void login(int adminId, String token, String ip) {
        LambdaUpdateWrapper<SysAdmin> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(SysAdmin::getLoginDate, DateUtil.now()).set(SysAdmin::getLoginIp, ip)
                .eq(SysAdmin::getId, adminId);
        this.update(updateWrapper);

        String key = LOGIN_PREFIX + adminId;
        LinkedList<String> tokenList = cacheManager.get(key);
        if (tokenList == null) {
            tokenList = new LinkedList<>();
        }
        //限制5个地点登录
        if (tokenList.size() >= LOGIN_LIMIT) {
            tokenList.poll();
        }
        tokenList.add(token);
        cacheManager.set(key, tokenList, MAX_LIFE);
    }

    @Override
    public void logout(int adminId, String token) {
        String key = LOGIN_PREFIX + adminId;
        LinkedList<String> tokenList = cacheManager.get(key);
        if (tokenList != null) {
            tokenList.remove(token);
        }
        cacheManager.set(key, tokenList, MAX_LIFE);
    }

    @Override
    public boolean isLogin(int adminId, String token) {
        String key = LOGIN_PREFIX + adminId;
        LinkedList<String> tokenList = cacheManager.get(key);
        if (tokenList != null) {
            return tokenList.contains(token);
        }
        return false;
    }

    @Override
    public void renewLogin(int adminId, String token) {
        String key = LOGIN_PREFIX + adminId;
        cacheManager.expire(key, MAX_LIFE);
    }

    @Override
    public String createJwt(SysAdmin sysAdmin) {
        if (sysAdmin == null) {
            return null;
        }
        Map<String, Object> claims = new HashMap<>(7);
        claims.put(SysAdminService.ID, sysAdmin.getId());
        claims.put(SysAdminService.USERNAME, sysAdmin.getUsername());
        claims.put(SysAdminService.MOBILE, sysAdmin.getMobile());
        claims.put(SysAdminService.NICKNAME, sysAdmin.getNickname());
        claims.put(SysAdminService.EMAIL, sysAdmin.getEmail());
        claims.put(SysAdminService.PASSWORD, sysAdmin.getPassword());
        claims.put(SysAdminService.ROLE_ID, sysAdmin.getRoleId());
        return JwtUtil.createJwt(claims, appConfig.getJwtSubject(), appConfig.getJwtSecret(), appConfig.getJwtLife());
    }

    @Override
    public SysAdmin getByMobile(String mobile) {
        return getOne(new LambdaQueryWrapper<SysAdmin>().eq(SysAdmin::getMobile, mobile));
    }

    @Override
    public SysAdmin getByMobileAndUsername(String mobile, String username) {
        return getOne(new LambdaQueryWrapper<SysAdmin>()
                .and(a -> a.eq(SysAdmin::getMobile, mobile))
                .or(b -> b.eq(SysAdmin::getUsername, username))
        );
    }

    @Override
    public SysAdmin getByEmail(String email) {
        if (CharSequenceUtil.isEmpty(email)) {
            return null;
        }
        return this.getOne(new LambdaQueryWrapper<SysAdmin>().eq(SysAdmin::getEmail, email));
    }

    @Override
    public SysAdmin getByUsername(String username) {
        if (CharSequenceUtil.isEmpty(username)) {
            return null;
        }
        return this.getOne(new LambdaQueryWrapper<SysAdmin>().eq(SysAdmin::getUsername, username));
    }

    @Override
    public String encodePassword(String password, String salt) {
        return SecureUtil.md5(password + salt);
    }

    @Override
    public List<SysAdmin> listByIdIn(List<Integer> sysAdminIdList) {
        if (CollUtil.isEmpty(sysAdminIdList)) {
            return new ArrayList<>();
        }
        return this.listByIds(sysAdminIdList);
    }

    @Override
    public List<SysAdmin> searchByNameOrMobile(String keywords) {
        return this.list(new LambdaQueryWrapper<SysAdmin>()
                .like(SysAdmin::getNickname, keywords)
        );
    }

    @Override
    public String adminImport(List<SysAdmin> userImportList) {
        // TODO 用户信息更新逻辑
        return "导入完成";
    }

    @Override
    public Map<Integer, SysAdmin> mapByIdList(List<Integer> adminIdList) {
        List<SysAdmin> sysAdminList = this.listByIdIn(adminIdList);
        if (CollUtil.isEmpty(sysAdminList)) {
            return new HashMap<>();
        }
        return sysAdminList.stream().collect(Collectors.toMap(SysAdmin::getId, a -> a, (e, s) -> e));
    }

    private void checkPermission(int adminId, int setRoleId, int setRusticId) {
        SysAdmin admin = this.getById(adminId);
        if (admin == null) {
            throw new JzRuntimeException(SysAdminService.SYS_ADMIN_NOT_EXIST);
        }
        if (RoleEnum.isSuperAdmin(setRoleId)) {
            throw new JzRuntimeException("这里不能操作超级管理员账号");
        }
        if (admin.getRoleId() != RoleEnum.SUPER_ADMIN.getId() && admin.getRoleId() != RoleEnum.RUSTIC_ADMIN.getId()) {
            throw new JzRuntimeException("权限不足");
        }
        // FIXME 判断该管理员是否该乡村的乡村管理员,后期直接设置拦截器
        RusticAdmin rusticAdmin = rusticAdminService.getByAdminIdAndRusticId(adminId, setRusticId);
        if ((rusticAdmin == null || rusticAdmin.getRoleId() != RoleEnum.RUSTIC_ADMIN.getId())
                && admin.getRoleId() != RoleEnum.SUPER_ADMIN.getId()) {
            throw new JzRuntimeException("权限不足");
        }
    }

    @Override
    public void addRusticOrOperation(SysAdminAddDTO sysAdminAddDTO, int adminId) {
        // 判断是否有权限
        this.checkPermission(adminId, sysAdminAddDTO.getRoleId(), sysAdminAddDTO.getRusticId());
        SysAdmin sysAdmin = this.getByUsername(sysAdminAddDTO.getUsername());
        if (sysAdmin != null) {
            throw new JzRuntimeException("该管理员名已被注册");
        }
        sysAdmin = sysAdminAddDTO.toSysAdmin();
        String salt = RandomUtil.randomString(16);
        String password = this.encodePassword(sysAdminAddDTO.getPassword(), salt);
        sysAdmin.setSalt(salt);
        sysAdmin.setPassword(password);
        sysAdmin.setStatus(DisableStatus.NORMAL.getValue());
        // 不是乡村管理员的角色2统一添加为运营管理员角色3
        if (sysAdminAddDTO.getRoleId() != RoleEnum.RUSTIC_ADMIN.getId()) {
            sysAdmin.setRoleId(RoleEnum.OPERATION_ADMIN.getId());
        }
        this.save(sysAdmin);
        // 判断rusticId是否存在
        Rustic rustic = rusticService.getById(sysAdminAddDTO.getRusticId());
        if (rustic == null) {
            throw new JzRuntimeException(RusticService.RUSTIC_NOT_EXIST);
        }
        // 创建管理员与乡村关联
        List<Integer> rusticIdList = new ArrayList<>();
        rusticIdList.add(sysAdminAddDTO.getRusticId());
        rusticAdminService.saveLinkBatch(sysAdmin.getId(), rusticIdList, sysAdminAddDTO.getRoleId());
    }

    @Override
    public void updateRusticOrOperation(SysAdminUpdateDTO sysAdminUpdateDTO, int adminId) {
        // 判断是否有权限
        this.checkPermission(adminId, sysAdminUpdateDTO.getRoleId(), sysAdminUpdateDTO.getRusticId());
        SysAdmin sysAdmin = this.getById(sysAdminUpdateDTO.getId());
        if (sysAdmin == null) {
            throw new JzRuntimeException(SysAdminService.SYS_ADMIN_NOT_EXIST);
        }
        SysAdmin username = this.getByUsername(sysAdminUpdateDTO.getUsername());
        if (username != null && !Objects.equals(username.getId(), sysAdmin.getId())) {
            throw new JzRuntimeException("该账号已被使用");
        }
        // 不复制密码
        BeanUtil.copyProperties(sysAdminUpdateDTO, sysAdmin, "password");
        if (CharSequenceUtil.isNotEmpty(sysAdminUpdateDTO.getPassword())) {
            String password = sysAdmin.getPassword();
            String salt = sysAdmin.getSalt();
            if (CharSequenceUtil.isNotEmpty(sysAdminUpdateDTO.getPassword())) {
                salt = RandomUtil.randomString(16);
                password = this.encodePassword(sysAdminUpdateDTO.getPassword(), salt);
            }
            sysAdmin.setPassword(password);
            sysAdmin.setSalt(salt);
        }
        SysRole sysRole = sysRoleService.getById(sysAdminUpdateDTO.getRoleId());
        if (sysRole == null) {
            throw new JzRuntimeException(SysRoleService.SYS_ROLE_NOT_EXIST);
        }
        // 不是乡村管理员的角色2统一添加为运营管理员角色3
        if (sysAdminUpdateDTO.getRoleId() != RoleEnum.RUSTIC_ADMIN.getId()) {
            sysAdmin.setRoleId(RoleEnum.OPERATION_ADMIN.getId());
        }
        if (rusticAdminService.getAdminRoleMax(sysAdmin.getId()) == RoleEnum.RUSTIC_ADMIN.getId()) {
            sysAdmin.setRoleId(RoleEnum.RUSTIC_ADMIN.getId());
        }
        this.updateById(sysAdmin);
        // 判断rusticId是否存在
        Rustic rustic = rusticService.getById(sysAdminUpdateDTO.getRusticId());
        if (rustic == null) {
            throw new JzRuntimeException(RusticService.RUSTIC_NOT_EXIST);
        }
        // 更新管理员与乡村关联
        rusticAdminService.updateLink(sysAdmin.getId(), sysAdminUpdateDTO.getRusticId(), sysAdminUpdateDTO.getRoleId());
    }

    @Override
    public RusticAdminDetailVO getRusticAdmin(int adminId, int rusticId) {
        SysAdmin sysAdmin = this.getById(adminId);
        if (sysAdmin == null) {
            throw new JzRuntimeException("账户不存在");
        }
        RusticAdminDetailVO rusticAdminDetailVO = new RusticAdminDetailVO(sysAdmin);
        rusticAdminDetailVO.setRusticId(rusticId);
        if (RoleEnum.isSuperAdmin(sysAdmin.getRoleId())) {
            // 超管
            rusticAdminDetailVO.setRusticRoleId(sysAdmin.getRoleId());
            setPermission(sysAdmin.getRoleId(), rusticAdminDetailVO);
        } else {
            RusticAdmin rusticAdmin = rusticAdminService.getByAdminIdAndRusticId(adminId, rusticId);
//            rusticAdminDetailVO.setRusticRoleId(rusticAdmin != null ? rusticAdmin.getRoleId() : 0);
            rusticAdminDetailVO.setRusticRoleId(0);
            rusticAdminDetailVO.setPermissionList(new ArrayList<>());
            if (rusticAdmin != null) {
                rusticAdminDetailVO.setRusticRoleId(rusticAdmin.getRoleId());
                setPermission(rusticAdmin.getRoleId(), rusticAdminDetailVO);
            }
        }
        return rusticAdminDetailVO;
    }

    private void setPermission(int roleId, RusticAdminDetailVO rusticAdminDetailVO) {
        // 添加权限
        List<SysPermission> permissionList = new ArrayList<>();
        SysRoleVO roleVO = null;
        if (roleId > 0) {
            SysRole role = sysRoleService.getById(roleId);
            if (role != null) {
                roleVO = new SysRoleVO(role);
                List<Long> permissionIdList = Arrays.stream(role.getPermissions().split(Consts.COMMA)).map
                        (Long::parseLong).collect(Collectors.toList());
                permissionList = sysPermissionService.getPermissionListByIdIn(permissionIdList);
            }
        }

        rusticAdminDetailVO.setPermissionList(sysPermissionService.generateTree(permissionList));
        rusticAdminDetailVO.setRole(roleVO);
    }

    @Override
    public List<SysAdmin> listByIdsAndUsername(List<Integer> adminIdList, String username) {
        return this.list(new LambdaQueryWrapper<SysAdmin>()
                .in(CollUtil.isNotEmpty(adminIdList), SysAdmin::getId, adminIdList)
                .like(CharSequenceUtil.isNotEmpty(username), SysAdmin::getUsername, username));
    }

}
